package cn.com.spring.auto;


import cn.com.mybaits.MybatisFactory;
import cn.com.mybaits.annotation.Mapper;
import cn.com.spring.BeanFactory;
import cn.com.spring.annotation.Aspect;
import cn.com.spring.annotation.Autowired;
import cn.com.spring.annotation.BeanScan;
import cn.com.spring.annotation.Component;
import cn.com.spring.annotation.MybatisScan;
import cn.com.spring.enums.AspectEnum;
import cn.com.utils.AopUtil;
import cn.com.utils.ClassScannerUtil;
import cn.com.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Objects;

/**
 * 应用
 *
 * @author LiuGuodong
 * @date 2023/10/07
 */
@Slf4j
public class Application {

    public static <T> void run(Class<T> appClass) throws Exception {
        handleMybatis(appClass);
        handleSpring(appClass);
    }

    /**
     * 处理mybatis
     *
     * @param appClass 应用程序类
     * @throws Exception 例外
     */
    private static <T> void handleMybatis(Class<T> appClass) throws Exception {
        MybatisScan mybatisScan = appClass.getAnnotation(MybatisScan.class);
        if (Objects.nonNull(mybatisScan) && Objects.nonNull(mybatisScan.value())) {
            List<Class> classes = ClassScannerUtil.scanClasses(mybatisScan.value(), Mapper.class);
            for (Class<?> aClass : classes) {
                Object mapper = MybatisFactory.getMapper(aClass);
                MybatisFactory.setBean(aClass.getSimpleName(), mapper);
            }
            BeanFactory.setBeanMap(MybatisFactory.getMapper());
        }
    }

    /**
     * 处理Spring
     *
     * @param appClass 应用程序类
     * @throws Exception 例外
     */
    private static <T> void handleSpring(Class<T> appClass) throws Exception {
        BeanScan beanScan = appClass.getAnnotation(BeanScan.class);
        if (Objects.nonNull(beanScan) && Objects.nonNull(beanScan.value())) {
            List<Class> classes = ClassScannerUtil.scanClasses(beanScan.value(), Component.class);
            for (Class<?> aClass : beanScan.exclude()) {
                classes.remove(aClass);
            }
            for (Class<?> aClass : classes) {
                createObject(aClass);
            }
        }
    }

    /**
     * 处理bean
     *
     * @param aClass 一个班
     * @throws Exception 例外
     */
    private static void createObject(Class<?> aClass) throws Exception {
        if (Modifier.isInterface(aClass.getModifiers())) {
            return;
        }
        Object targetObject = AopUtil.getAspectProxy(aClass);
        //切面逻辑处理
        if (handleAspect(aClass)) {
            return;
        }
        //放入工厂
        BeanFactory.saveToFactory(aClass, targetObject);

        //属性注入
        attributeInjection(targetObject);

    }

    /**
     * 处理切面
     *
     * @param aClass 一个班
     * @return boolean
     * @throws InstantiationException 实例化异常
     * @throws IllegalAccessException 非法访问异常
     */
    private static boolean handleAspect(Class<?> aClass) throws Exception {
        Aspect aspect = aClass.getAnnotation(Aspect.class);
        if (Objects.nonNull(aspect)) {
            Object bean = aClass.newInstance();
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {
                saveAspect(method, bean);
                //属性注入
                attributeInjection(bean);
            }
            return true;
        }
        return false;
    }

    /**
     * 保存切面
     *
     * @param method 方法
     * @param bean   豆
     */
    private static void saveAspect(Method method, Object bean) {
        for (AspectEnum value : AspectEnum.values()) {
            Annotation annotation = method.getAnnotation(value.getValue());
            if (annotation != null) {
                BeanFactory.setAspect(getAnnotationName(annotation), bean);
            }
        }
    }

    /**
     * 获取注释name方法的值
     *
     * @param annotation 注释
     * @return {@link String}
     */
    private static String getAnnotationName(Annotation annotation) {
        try {
            Method nameMethod = annotation.annotationType().getMethod("name");
            String name = ((Class<?>) nameMethod.invoke(annotation)).getName();
            if (StringUtil.isBlank(name)) {
                nameMethod = annotation.annotationType().getMethod("value");
                name = (String) nameMethod.invoke(annotation);
                if (StringUtil.isBlank(name)) {
                    throw new RuntimeException("");
                }
            }
            return name;
        } catch (Exception e) {
            log.error("获取注释name方法的值失败", e);
            return "";
        }
    }


    private static void attributeInjection(Object targetObject) throws Exception {
        Field[] fields = targetObject.getClass().getDeclaredFields();
        for (Field field : fields) {
            Autowired autowired = field.getAnnotation(Autowired.class);
            if (Objects.isNull(autowired)) {
                continue;
            }
            Object bean = BeanFactory.getBean(field.getType().getSimpleName());
            //未代理
            if (Objects.isNull(bean)) {
                createObject(field.getType());
                bean = BeanFactory.getBean(field.getType().getSimpleName());
            }
            field.setAccessible(true);
            field.set(targetObject, bean);
        }
    }
}
